package com.tea.utils;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.tea.config.wx.WxShippingConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * 微信工具类
 * 提供微信相关的通用功能，包括token获取和重试机制
 */
@Component
@Slf4j
public class WxUtil {

    @Resource
    private WxShippingConfig wxShippingConfig;

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 获取微信Access Token
     * @return Access Token
     */
    public String getAccessToken() {
        return getAccessToken(false);
    }

    /**
     * 获取微信Access Token
     * @param forceRefresh 是否强制刷新token
     * @return Access Token
     */
    public String getAccessToken(boolean forceRefresh) {
        String cacheKey = "wx_access_token";
        String token = null;

        if (!forceRefresh) {
            token = redisTemplate.opsForValue().get(cacheKey);
        }

        if (StrUtil.isBlank(token) || forceRefresh) {
            try {
                // 请求微信API获取token
                String url = String.format("%s?grant_type=client_credential&appid=%s&secret=%s",
                        wxShippingConfig.getTokenUrl(),
                        wxShippingConfig.getAppId(),
                        wxShippingConfig.getAppSecret());

                log.info("请求微信AccessToken{}: {}", forceRefresh ? "(强制刷新)" : "", url.replaceAll("secret=[^&]*", "secret=***"));

                HttpResponse response = HttpRequest.get(url)
                        .timeout(10000)
                        .execute();

                if (response.isOk()) {
                    String responseBody = response.body();
                    JSONObject result = JSON.parseObject(responseBody);

                    if (result.containsKey("access_token")) {
                        token = result.getString("access_token");
                        // 缓存token，有效期7000秒（提前200秒过期）
                        redisTemplate.opsForValue().set(cacheKey, token, 7000, TimeUnit.SECONDS);
                        log.info("获取微信AccessToken成功{}", forceRefresh ? "(已强制刷新)" : "");
                    } else {
                        log.error("获取微信AccessToken失败: {}", responseBody);
                        throw new RuntimeException("获取微信AccessToken失败: " + result.getString("errmsg"));
                    }
                } else {
                    log.error("请求微信AccessToken失败，HTTP状态码: {}", response.getStatus());
                    throw new RuntimeException("请求微信AccessToken失败");
                }

            } catch (Exception e) {
                log.error("获取微信AccessToken异常", e);
                throw new RuntimeException("获取微信AccessToken异常: " + e.getMessage());
            }
        }

        return token;
    }

    /**
     * 带token重试机制的执行方法
     * 当检测到access_token失效时，会自动刷新token并重试操作
     * @param operation 要执行的操作
     * @return 操作结果
     */
    public Result<?> executeWithTokenRetry(Supplier<Result<?>> operation) {
        try {
            // 第一次尝试
            Result<?> result = operation.get();
            return result;
        } catch (RuntimeException e) {
            // 检查是否是access_token失效错误
            if (e.getMessage() != null && e.getMessage().startsWith("ACCESS_TOKEN_INVALID")) {
                log.info("检测到access_token失效，清除缓存并重试");

                // 清除Redis中的token缓存
                String cacheKey = "wx_access_token";
                redisTemplate.delete(cacheKey);

                try {
                    // 强制刷新token并重试
                    getAccessToken(true);
                    Result<?> retryResult = operation.get();
                    log.info("token刷新后重试成功");
                    return retryResult;
                } catch (Exception retryException) {
                    log.error("token刷新后重试仍然失败", retryException);
                    return Result.error("操作失败，重试后仍然失败: " + retryException.getMessage());
                }
            } else {
                // 其他异常直接抛出
                throw e;
            }
        }
    }
}